package cn.engage.basic.service.impl;

import cn.engage.basic.config.BussinessException;
import cn.engage.basic.constant.BaseConstant;
import cn.engage.basic.dto.LoginDto;
import cn.engage.basic.dto.LoginPhoneCodeDto;
import cn.engage.basic.dto.WechatLoginDto;
import cn.engage.basic.jwt.JwtUtils;
import cn.engage.basic.jwt.RsaUtils;
import cn.engage.basic.jwt.UserInfo;
import cn.engage.basic.service.LoginService;
import cn.engage.basic.util.AjaxResult;
import cn.engage.basic.util.HttpUtil;
import cn.engage.basic.util.MD5Utils;
import cn.engage.basic.util.StrUtils;
import cn.engage.basic.vo.LoginVo;
import cn.engage.system.domain.Menu;
import cn.engage.system.mapper.MenuMapper;
import cn.engage.system.mapper.PermissionMapper;
import cn.engage.user.domain.Logininfo;
import cn.engage.user.domain.User;
import cn.engage.user.domain.Wxuser;
import cn.engage.user.mapper.LogininfoMapper;
import cn.engage.user.mapper.UserMapper;
import cn.engage.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.security.PrivateKey;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

@Service
public class LoginServiceImpl implements LoginService {
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxuserMapper wxuserMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private MenuMapper menuMapper;
    @Value("${jwt.rsa.pri}")
    private String privateRsaKey;
    /* jwt登录*/
    private LoginVo getLoginVo(Logininfo logininfo) {
        // 使用jwt方案
        PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource(privateRsaKey)
                .getFile());
        LoginVo loginVo = new LoginVo();
        UserInfo user = new UserInfo();
        user.setLogininfo(logininfo);
        if (0 == logininfo.getType()){
            // 如果是后端 那么我们需要将权限放到token中
            // 菜单权限
            List<String> permissions = permissionMapper.findByLogininfoId(logininfo.getId());
            // 按钮权限
            List<Menu> menus = menuMapper.findByLogininfoId(logininfo.getId());
            user.setPermissions(permissions);
            user.setMenus(menus);
            loginVo.setPermissions(permissions);
            loginVo.setMenus(menus);

        }
        String token = JwtUtils.generateTokenExpireInMinutes(user, privateKey, 300);
        // 将logininfo敏感字段置空
        logininfo.setPassword("");
        logininfo.setSalt("");
        logininfo.setPhone("");
        loginVo.setToken(token);
        loginVo.setLogininfo(logininfo);
        // 返回给前端
        return loginVo;
    }


    // 账号密码登录
    @Override
    public LoginVo login(LoginDto dto) {
        // 查询是否有该账号
        Logininfo logininfo = logininfoMapper.getByLogin(dto.getUsername(), dto.getType());
        if (logininfo == null){
            throw new BussinessException("账号或密码错误");
        }
        // 判断密码是否正确
        String passW = MD5Utils.encrypByMd5(logininfo.getSalt() + dto.getPassword());
        if (StringUtils.isBlank(passW) || !passW.equals(logininfo.getPassword())){
            throw new BussinessException("账号或密码错误");
        }
        /* 将token和logininfo保存到redis中返回前端，在拦截器中根据前端传过来的token来判断该用户是否登录 */
        return getLoginVo(logininfo);
    }



    /* 手机验证码登录获取验证码 */
    @Override
    public void phoneCode(LoginPhoneCodeDto dto) {
       // 判断手机号格式是否正确
        if(!Pattern.matches("^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$",dto.getPhone())){
            throw new BussinessException("手机号格式错误");
        }
        // 判断该手机号是否注册
        Logininfo logininfo = logininfoMapper.findByPhone(dto.getPhone());
        if (logininfo == null){
            throw new BussinessException("该手机号未注册，请先注册");
        }
        String redis = (String) redisTemplate.opsForValue().get(dto.getImageCodeKey() + dto.getPhone());
        String randomString = "";
        if (StringUtils.isBlank(redis)){
            randomString = StrUtils.getRandomString(4);
            // 判断手机号发送时间是否大于1分钟
        }else {
            Long time = redisTemplate.getExpire(dto.getImageCodeKey() + dto.getPhone(), TimeUnit.SECONDS);
            if (time >= 240){
                throw new BussinessException("您已获取过验证码请"+(time - 240)+"秒后重试");
            }
            randomString = StrUtils.getRandomString(4);
        }
        // 获取四位数的随机数

        // 存入到redis中时间为5分钟
        redisTemplate.opsForValue().set(dto.getImageCodeKey() + dto.getPhone(),randomString,5,TimeUnit.MINUTES);
        System.err.println(randomString);
        // 调用工具类发送手机验证码
        // SendSmsUtil.send(dto.getPhone(),"你好：你的验证码为："+randomString+"，5分钟内有效！");
    }

    /* 手机验证码登录 */
    @Override
    public LoginVo loginOnPhone(LoginPhoneCodeDto dto) {
        if (StringUtils.isBlank(dto.getImageCodeKey())){
            throw new BussinessException("验证码不能为空");
        }
        String s = (String) redisTemplate.opsForValue().get(dto.getImageCodeKey() + dto.getPhone());
        System.err.println(s);
        System.err.println(dto.getImageCodeKey() + dto.getPhone());
        if (!s.equals(dto.getSmsCode())){
            throw new BussinessException("验证码输入错误");
        }
        Logininfo logininfo = logininfoMapper.findByPhone(dto.getPhone());

        return getLoginVo(logininfo);
    }

    /* 微信扫描登录第一步，前端传入一个code */
    @Override
    public AjaxResult wechatLoginOne(WechatLoginDto dto) {
        // 判断code是否为空
        if (StringUtils.isBlank(dto.getCode())){
            throw new BussinessException("code为空");
        }
        /* 根据code调用微信第二个接口获取access_token和openid */
        Pair<String, String> pair = getTokenAndOpenid(dto);

        // 去wxuser中查看是否注册过
        Wxuser wxuser = wxuserMapper.findByOpenid(pair.getRight());
        // 已经注册 登录过
        if (wxuser != null && wxuser.getUserId() != null){
            User user = userMapper.findByIdOne(wxuser.getUserId());
            Logininfo logininfo = logininfoMapper.findByIdOne(user.getLogininfoId());
            /* 保存到redis中，时间为30分钟 */
            LoginVo loginVo = this.getLoginVo(logininfo);
            return AjaxResult.me().success(loginVo);
        }
        // 第一次登录没有绑定手机号码
        StringBuffer stringBuffer = new StringBuffer()
                .append("?accessToken=")
                .append(pair.getLeft())
                .append("&openId=").append(pair.getRight());
        return AjaxResult.me().fail("nobinder").setResultObj(stringBuffer);
    }
    /* 微信扫描登录第二步，绑定手机号存入数据库 */
    @Override
    public LoginVo wechatBinder(WechatLoginDto dto) {
        // 参数校验
        extracted(dto);
        /* 请求微信第三个接口获取用户数据 */
        String url = String.format(BaseConstant.wxLogin.WX_LOGIN_USER_INFO, dto.getAccessToken(), dto.getOpenId());
        String s = HttpUtil.httpGet(url);
        // 将获取的json对象转化为对应的实体类，字段名得对应才能接收
        Wxuser wxuser = JSONObject.parseObject(s, Wxuser.class);
        // 查询该手机是否已经注册
        User user = userMapper.findByPhone(dto.getPhone());
        // 已经注册了就添加一个表wxuser，并且加上userid
        Logininfo logininfo = new Logininfo();
        if (user != null){
            logininfo = logininfoMapper.findByIdOne(user.getId());
        }else {
            // 保存数据到logininfo和user
            user = getUser(dto, logininfo);
        }
        // 保存到wxuser表
        wxuser.setUserId(user.getId());
        wxuserMapper.add(wxuser);
        return getLoginVo(logininfo);
    }


    private void extracted(WechatLoginDto dto) {
        if (StringUtils.isBlank(dto.getPhone())){
            throw new BussinessException("手机号不能为空");
        }
        if (StringUtils.isBlank(dto.getVerifyCode())){
            throw new BussinessException("验证码不能为空");
        }
        String verCode = (String) redisTemplate.opsForValue().get(String.format(BaseConstant.VerfityCodeCons.REGISTER_SMSCODE_PHONE, dto.getPhone()));
        if (!dto.getVerifyCode().equals(verCode)){
                throw new BussinessException("验证码错误");
        }
    }

    @NotNull
    private User getUser(WechatLoginDto dto, Logininfo logininfo) {
        // 没注册就注册添加到三个表中,设置初始密码为1,给密码加密
        String salt = StrUtils.getComplexRandomString(32);
        String passWord = salt + "1";
        String md5PassWord = MD5Utils.encrypByMd5(passWord);
        // 给user赋值
        User user = User.builder()
                  .password(md5PassWord)
                  .createtime(new Date())
                  .state(1)
                  .salt(salt)
                  .phone(dto.getPhone())
                  .username(dto.getPhone())
                  .build();
        // 给logininfo赋值
        BeanUtils.copyProperties(user, logininfo);
        logininfo.setType(1);
        // 保存logininfo
        logininfoMapper.add(logininfo);
        // 保存user
        user.setLogininfoId(logininfo.getId());
        userMapper.add(user);
        return user;
    }

    // token登录
//    @NotNull
//    private LoginVo getLoginVo(Logininfo logininfo) {
//        // 获取token
//        String token = String.valueOf(UUID.randomUUID());
//        // 保存到redis中
//        redisTemplate.opsForValue().set(token, logininfo,30, TimeUnit.MINUTES);
//        // 将logininfo敏感字段置空
//        logininfo.setPassword("");
//        logininfo.setSalt("");
//        logininfo.setPhone("");
//        return new LoginVo(token, logininfo);
//    }


    private Pair<String, String> getTokenAndOpenid(WechatLoginDto dto) {
        // 拼接url地址
        String url = String.format(BaseConstant.wxLogin.WX_LOGIN_TOKEN_URL, BaseConstant.wxLogin.WX_LOGIN_APPID
                , BaseConstant.wxLogin.WX_LOGIN_SECRET, dto.getCode());
        // 调用httpUtil发送get请求
        String httpGet = HttpUtil.httpGet(url);
        // httpGet获取到的是json字符串转换为json对象
        JSONObject jsonObject = JSONObject.parseObject(httpGet);
        // 获取access_token和openid
        String accessToken = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");
        // pair可以装两个对象
        Pair<String, String> pair = Pair.of(accessToken, openid);
        return pair;
    }


}
